package lp.polimi.treefinder.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import lp.polimi.treefinder.R;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;

public class FilesUtils {


	private static final int JPEG_COMPRESSION_LEVEL = 100;
	private static final int REDUCED_SMALLEST_DIM = 300;


	public static enum SaveMode {
		JPEG, JPEG_SQUARED, PNG;
	}

	private FilesUtils () {

	}

	public static byte[] retrieveLeafBytes (Context context, String leafName, String extension) throws IOException {
		File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), leafName+"."+extension);
		FileInputStream fis = new FileInputStream(file);
		byte[] bytes = new byte[(int)file.length()];
		fis.read(bytes);
		fis.close();
		return bytes;
	}

	public static void saveLeafBytes (Context context, byte[] image, String leafName, SaveMode mode) throws IOException {
		switch (mode) {
		case PNG:
			ByteArrayInputStream bis = new ByteArrayInputStream(image);
			toAndroidFile(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), bis,  leafName+".png");
			break;
		case JPEG_SQUARED:
			Bitmap bitmapImage = BitmapFactory.decodeByteArray(image, 0, image.length);
			int maxDim = Math.max(bitmapImage.getWidth(), bitmapImage.getHeight());
			Bitmap newBitmap = Bitmap.createBitmap(maxDim, maxDim, bitmapImage.getConfig());
			Canvas canvas = new Canvas(newBitmap);
			canvas.drawColor(Color.WHITE);
			if (bitmapImage.getWidth()<bitmapImage.getHeight())
				canvas.drawBitmap(bitmapImage, (maxDim-bitmapImage.getWidth())/2, 0, null);
			else
				canvas.drawBitmap(bitmapImage, 0, (maxDim-bitmapImage.getHeight())/2, null);
			FileOutputStream fos = new FileOutputStream(new File (context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), leafName+".jpg"));
			newBitmap.compress(CompressFormat.JPEG, JPEG_COMPRESSION_LEVEL, fos);
			fos.close();
			break;
		case JPEG:
			FileOutputStream fos2 = new FileOutputStream(new File (context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), leafName+".jpg"));
			BitmapFactory.decodeByteArray(image, 0, image.length).compress(CompressFormat.JPEG, JPEG_COMPRESSION_LEVEL, fos2);
			fos2.close();
		}
	}


	public static Bitmap retrieveLeafBitmap (Context context, String leafName, String extension) {	
		return BitmapFactory.decodeFile(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)+"/"+leafName+"."+extension);
	}

	public static Uri retrieveLeafUri (Context context, String leafName, String extension) {
		return Uri.fromFile(new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)+"/"+leafName+"."+extension));
	}


	public static void saveLeafResource (Context context, int leafResourceID, String leafName) throws IOException {
		resourceToAndroidFile(context, context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), leafName, "png", leafResourceID);
	}

	public static void resourceToAndroidFile (Context context, File fileDir, String outputFileName, String fileExtension, int inputResourceID) throws IOException {
		InputStream is = context.getResources().openRawResource(inputResourceID);
		toAndroidFile(fileDir, is, outputFileName+"."+fileExtension);
	}

	private static void toAndroidFile (File fileDir, InputStream is, String outputFileName) throws IOException { 
		FileOutputStream fout = new FileOutputStream(fileDir+"/"+outputFileName);
		byte[] data = new byte[is.available()];
		is.read(data);
		fout.write(data);
		is.close();
		fout.close();
	}

	public static void deleteLeafImage(Context context, String fileName) {
		File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName+".png");
		if (file != null) {
			file.delete();
		}
	}


	public static void deleteLeafTags (Context context) throws IOException {

		File file = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
		if (file==null)
			throw new IOException("External storage is not mounted.");
		File[] leafImages = file.listFiles();
		for (int i = 0; i < leafImages.length; i++) {
			leafImages[i].delete();
		}
		//delete xml
		File xml = new File(context.getExternalFilesDir(null), "leaves.xml");
		xml.delete();
		setupLeavesXML(context, false);
		setupUnknownLeafImage(context);
	}


	public static void setupLeavesXML(Context context, boolean isDebug) throws IOException {

		if (!isDebug) {
			File xml = new File(context.getExternalFilesDir(null), "leaves.xml");
			if (!xml.exists())
				resourceToAndroidFile(context, context.getExternalFilesDir(null), "leaves", "xml", R.raw.leaves_clean);
		} else
			resourceToAndroidFile(context, context.getExternalFilesDir(null), "leaves", "xml", R.raw.leaves);
	}

	public static void setupUnknownLeafImage (Context context) throws IOException {
		if (!(new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)+"/unknown_leaf.png").exists())) {
			InputStream is = context.getAssets().open("unknown_leaf.png");
			toAndroidFile(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), is, "unknown_leaf.png");
		}
	}


	public static String getRealPathFromURI(Context context, Uri contentUri) {
		Cursor cursor = null;
		try { 
			String[] proj = { MediaStore.Images.Media.DATA };
			cursor = context.getContentResolver().query(contentUri,  proj, null, null, null);
			int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
			cursor.moveToFirst();
			return cursor.getString(column_index);
		} finally {
			if (cursor != null) {
				cursor.close();
			}
		}
	}



	private static int calculateInSampleSize(BitmapFactory.Options options) {

		final int height = options.outHeight;
		final int width = options.outWidth;
		int reqWidth;
		int reqHeight;
		if (height<=width) {
			reqHeight = REDUCED_SMALLEST_DIM;
			reqWidth = width*REDUCED_SMALLEST_DIM/height;
		} else {
			reqWidth = REDUCED_SMALLEST_DIM;
			reqHeight = height*REDUCED_SMALLEST_DIM/width;
		}

		int inSampleSize = 1;

		if (height > reqHeight || width > reqWidth) {

			final int halfHeight = height / 2;
			final int halfWidth = width / 2;

			// Calculate the largest inSampleSize value that is a power of 2 and keeps both
			// height and width larger than the requested height and width.
			while ((halfHeight / inSampleSize) > reqHeight
					&& (halfWidth / inSampleSize) > reqWidth) {
				inSampleSize *= 2;
			}
		}

		return inSampleSize;
	}

	public static byte[] loadScaledBitmap(String filePath) {

		// First decode with inJustDecodeBounds=true to check dimensions
		final BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeFile(filePath, options);

		// Calculate inSampleSize
		options.inSampleSize = calculateInSampleSize(options);

		// Decode bitmap with inSampleSize set
		options.inJustDecodeBounds = false;
		return bitmapToBytes(BitmapFactory.decodeFile(filePath, options), CompressFormat.JPEG);
	}


	public static void saveUnknownLeaf(Context context, byte[] image, String uniqueIdentifier) throws IOException {
		saveLeafBytes(context, image, "unknownLeaf_"+uniqueIdentifier, SaveMode.JPEG);
	}

	public static byte[] retrieveUnknownLeaf (Context context, String uniqueIdentifier) throws IOException {
		return retrieveLeafBytes(context, "unknownLeaf_"+uniqueIdentifier, "jpg");
	}

	public static byte[] bitmapToBytes(Bitmap retr, CompressFormat format) {
		ByteArrayOutputStream stream = new ByteArrayOutputStream();
		retr.compress(format, 80, stream);
		return stream.toByteArray();
	}

	//	public static void saveUnknownLeaf(Context context, byte[] featuresBytes, int[] shapeDescriptorsBytes, String uniqueIdentifier) throws IOException {
	//
	//		if (featuresBytes!=null) {
	//			String featuresFilename = "unknownLeaf_"+uniqueIdentifier+"_features";
	//			DataOutputStream dos1 = new DataOutputStream(new FileOutputStream(new File(context.getFilesDir(), featuresFilename)));
	//			dos1.writeInt(featuresBytes.length);
	//			for (int i = 0; i < featuresBytes.length; i++) {
	//				dos1.writeByte(featuresBytes[i]);
	//			}
	//			dos1.close();
	//		}
	//
	//		if (shapeDescriptorsBytes!=null) {
	//			String shapeDescFilename = "unknownLeaf_"+uniqueIdentifier+"_shapeDesc";
	//			DataOutputStream dos2 = new DataOutputStream(new FileOutputStream(new File(context.getFilesDir(), shapeDescFilename)));
	//			dos2.writeInt(shapeDescriptorsBytes.length);
	//			for (int i = 0; i < shapeDescriptorsBytes.length; i++) {
	//				dos2.writeInt(shapeDescriptorsBytes[i]);
	//			}
	//			dos2.close();
	//		}
	//	}
	//	

	//	public static byte[] retrieveUnknownLeafFeatures (Context context, String uniqueIdentifier) throws IOException {
	//		String featuresFilename = "unknownLeaf_"+uniqueIdentifier+"_features";
	//		DataInputStream dis = new DataInputStream(new FileInputStream(new File(context.getFilesDir(), featuresFilename)));
	//		int l = dis.readInt();
	//		byte[] features = new byte[l];
	//		for (int i = 0; i < l; i++) {
	//			features[i] = dis.readByte();
	//		}
	//		dis.close();
	//		return features;
	//	}
	//	
	//	public static int[] retrieveUnknownLeafShapeDescriptors (Context context, String uniqueIdentifier) throws IOException {
	//		String featuresFilename = "unknownLeaf_"+uniqueIdentifier+"_shapeDesc";
	//		DataInputStream dis = new DataInputStream(new FileInputStream(new File(context.getFilesDir(), featuresFilename)));
	//		int l = dis.readInt();
	//		int[] shapeDescriptors = new int[l];
	//		for (int i = 0; i < l; i++) {
	//			shapeDescriptors[i] = dis.readInt();
	//		}
	//		dis.close();
	//		return shapeDescriptors;
	//	}


}
